home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-09-04 | 57.6 KB | 1,519 lines |
-
-
-
- - 1 -
-
-
-
- 9. _A_u_d_i_o__F_i_l_e__L_i_b_r_a_r_y
-
- This chapter lists information about the Audio File Library
- programming interface included in the the IRIS Digital Media
- Development Environment. It includes changes, additions,
- and bug fixes since the last release, and known problems,
- workarounds, and caveats.
-
- The new version of the Audio File Library consists of
- approximately 90 C routines that allow application programs
- to read/write audio sample data and auxiliary header
- information to/from a number of standard audio file formats.
- The library requires no special audio hardware at runtime,
- and is useful for sound file import/export/conversion
- operations as well as real-time recording/playback.
-
- The Audio File Library API allows you to access audio
- samples and nonaudio information from audio files using a
- common set of routines. The API includes functions for
- reading/writing various types of data including author,
- copyright, name, and annotation strings; sample frame marker
- locations; sample configuration parameters and loop points;
- MIDI exclusive data; and application-defined data.
-
- The Audio File Library implements transparent sample data
- format conversion and compression/decompression for several
- different audio file formats. The following compression
- algorithms are currently supported:
-
- +o MPEG-1 Layers I and II
-
- +o Aware Inc.'s, proprietary MultiRate algorithm
-
- +o CCITT G.711 mu-law and A-law
-
- +o CCITT G.722 ADPCM
-
- Transparent format conversion and compression/decompression
- means that application programs can process all audio files
- as uncompressed data in any format that the application
- desires. There is no need to write special code for
- handling different sample formats or types of compressed
- data. Compressed and decompressed files look the same to
- applications as long as the compression format is one of the
- supported types listed above.
-
- Note: The MPEG-1 encoder cannot be activated by an
- application without a FlexLM node-locked MPEG
- Encoder license, available from SGI.
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 2 -
-
-
-
- Note: The Aware MultiRate codec cannot be activated by an
- application without a FlexLM node-locked license,
- available from SGI.
-
- See the online man page _a_w_a_r_e(_5) for an overview of
- the Aware compression technology available to
- developers and end users. The online man page
- _a_f_I_n_i_t_C_o_m_p_r_e_s_s_i_o_n(3dm) describes the Audio File
- Library interface to reading/writing audio files that
- contain MultiRate or other types of compressed data.
-
- The Audio File Library API is intended to be general enough
- to allow support for additional digital audio file formats
- and compression schemes in the future. Applications that
- are developed on top of the current library will be able to
- read these additional file and data formats (with at most
- minor code modifications) after simply relinking with a new
- version of the Audio File Library. Existing applications
- will require slight modifications in order to write
- additional file and data formats.
-
- Applications linked with the new Audio File Library no
- longer need to be linked with any other library. All that
- is needed on the link line is:
-
- ----llllaaaauuuuddddiiiiooooffffiiiilllleeee
-
- The _I_R_I_S _D_i_g_i_t_a_l _M_e_d_i_a _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e contains an
- introduction to programming with the Audio File Library,
- along with a description of each of its functions. Please
- see section "Documentation Errors" for some important notes.
-
- The on-line man page _a_f_I_n_t_r_o(3dm) provides a brief overview
- of the library and a list of the available procedure calls.
-
- The IRIS Audio Utility Library is a small C language
- procedure call library that contains approximately 10
- auxiliary routines for use with the Audio File Library.
-
- Please see the _I_R_I_S _D_i_g_i_t_a_l _M_e_d_i_a _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e for an
- introduction to programming with Audio Utility Library and a
- detailed description of its functions.
-
- Currently, the library consists of the function
- _A_U_c_h_e_c_k_l_i_c_e_n_s_e(3dm), which allows an application to query
- license availability for Aware compressor/decompressor
- software on the current host, and several functions that
- implement a generalized parameter-value list object for use
- with the new Audio File Library routines
- _a_f_I_n_i_t_C_o_m_p_r_e_s_s_i_o_n_P_a_r_a_m_s(3dm) and
- _a_f_G_e_t_C_o_m_p_r_e_s_s_i_o_n_P_a_r_a_m_s(3dm). See the man page _A_U_p_v_n_e_w(3dm)
-
-
-
-
-
-
-
-
-
-
-
- - 3 -
-
-
-
- for a description of these routines.
-
- See the source code _a_i_f_c_c_o_n_v_e_r_t._c in the directory
- /_u_s_r/_s_h_a_r_e/_s_r_c/_d_m_e_d_i_a/_s_o_u_n_d_c_o_m_m_a_n_d_s for Audio Utility
- programming example.
-
-
- 9.1 _E_x_a_m_p_l_e__P_r_o_g_r_a_m_s__i_n__/_u_s_r_/_s_h_a_r_e_/_s_r_c_/_d_m_e_d_i_a_/_a_u_d_i_o
-
- The subsystems _d_m_e_d_i_a__d_e_v._s_r_c._t_o_o_l_s and
- _d_m_e_d_i_a__d_e_v._s_r_c._e_x_a_m_p_l_e_s contain several source code examples
- for programming with the Audio File Library. Executable
- versions of some of these programs are included as standard
- system utilities which can be installed from the
- _d_m_e_d_i_a__e_o_e._s_w._t_o_o_l_s subsystem included with the IRIX 6.2 O/S
- release. However, as noted in the Changes section below,
- most of these programs are now obsolete and are only
- provided as example code, and their functionality has been
- taken over by other programs.
-
- _p_l_a_y_a_i_f_c(1) and _r_e_c_o_r_d_a_i_f_c(1) are command-line programs for
- playing and recording AIFF-C/AIFF files, respectively. The
- programs are built on top of the IRIS Audio Library and the
- Audio File Library. The source code for these programs is
- available in
- /_u_s_r/_s_h_a_r_e/_s_r_c/_d_m_e_d_i_a/_s_o_u_n_d_c_o_m_m_a_n_d_s/{_p_l_a_y._c,_s_f_r_e_c_o_r_d._c}
-
- _a_i_f_c_i_n_f_o(1) is a command-line program that parses an AIFF-
- C/AIFF input file and prints a description of the data in
- the file, including characteristics of the audio sample
- data, and a summary of the nonaudio data (strings, loop
- points, and so on) found in the various file header fields.
- The source code is installed in
- /_u_s_r/_s_h_a_r_e/_s_r_c/_d_m_e_d_i_a/_s_o_u_n_d_c_o_m_m_a_n_d_s/_a_i_f_c_i_n_f_o._c.
-
- _a_i_f_f_2_a_i_f_c(1), _a_i_f_c_2_a_i_f_f(1), _a_i_f_c_c_o_m_p_r_e_s_s(1), and
- _a_i_f_c_d_e_c_o_m_p_r_e_s_s(1) are old command-line utility programs for
- converting between AIFF-C and AIFF files, or for converting
- AIFF-C files between compressed and uncompressed data
- formats. These utilities are all obsolete, as indicated
- below. The source for the program is the file
- /_u_s_r/_s_h_a_r_e/_s_r_c/_d_m_e_d_i_a/_s_o_u_n_d_c_o_m_m_a_n_d_s/_a_i_f_c_c_o_n_v_e_r_t._c.
-
- 9.2 _C_h_a_n_g_e_s__a_n_d__A_d_d_i_t_i_o_n_s
-
- This section lists changes/additions to the Audio File
- Library and accompanying example source code since its last
- release.
-
- +o The new AF allows transparent conversion of the audio
- data in a file's track into an uncompressed format of
-
-
-
-
-
-
-
-
-
-
-
- - 4 -
-
-
-
- the application's choice. This new format is known as
- the _v_i_r_t_u_a_l format of the data. The application need
- not concern itself with the data format in the file; it
- can specify the format in which it would like to
- receive the data. This also works in the other
- direction: A buffer of data in any uncompressed format
- may be written out to a file containing data in any
- other format (assuming the audio file type specified
- supports that format). Read the _a_f_I_n_t_r_o(3dm) man page
- for a detailed explanation of this procedure.
-
- +o The programming examples for the Audio File Library
- (and Audio Utility Library) are now located in the
- directories
- /_u_s_r/_s_h_a_r_e/_s_r_c/_d_m_e_d_i_a/{_s_o_u_n_d_c_o_m_m_a_n_d_s,_s_o_u_n_d_p_l_a_y_e_r,_a_u_d_i_o}.
-
- +o The following list of audio utility programs has been
- made obsolete by the new _d_m_c_o_n_v_e_r_t(3dm) and/or
- _d_m_i_n_f_o(3dm) utilities:
-
- aaaaiiiiffffffff2222aaaaiiiiffffcccc,,,, aaaaiiiiffffcccc2222aaaaiiiiffffffff,,,, aaaaiiiiffffccccccccoooommmmpppprrrreeeessssssss,,,, aaaaiiiiffffccccddddeeeeccccoooommmmpppprrrreeeessssssss,,,, aaaaiiiiffffcccciiiinnnnffffoooo
-
- For backwards compatibility, these utilities still
- exist as symbolic links to the new programs. SGI
- offers no guarantee that this will be continued in
- future releases.
-
- 9.3 _P_u_b_l_i_c__F_u_n_c_t_i_o_n__N_a_m_e__C_h_a_n_g_e_s
-
- The new AF has adopted a new standard for the naming of
- publically available routines. All Audio File Library
- routines now consist of the prefix "af" (in small case
- letters) followed by any number of words describing the
- function, with the first letter of each word capitalized.
- To allow existing code to run when linked against the new
- library, two forms of backwards- compatibility are built
- into the library:
-
- +o The public header file <dmedia/audiofile.h> contains a
- set of #define's which will cause the preprocessor to
- rename each occurrence of the old routine names in
- existing code when and if it is recompiled from source.
-
- +o For code which is not recompiled but which chooses to
- link against the 2.0 version of the library (see the
- section "How Does This DSO Magic Work?" below), a set
- of wrapper functions are contained within the library
- to allow the old routine symbol names to be resolved
- properly. For ease in debugging and maximum
- efficiency, it is recommended that programs be
- recompiled to allow the redefines to happen, or better
-
-
-
-
-
-
-
-
-
-
-
- - 5 -
-
-
-
- yet, rename the function calls using the new scheme.
-
- 9.4 _B_u_g__F_i_x_e_s
-
- Although the available functionality is the same, the 6.2
- version of the Audio File Library is a completely new DSO.
- Because of this, it would not make sense to describe its
- "bug fixes" vs. the old libaudiofile. Future release notes
- will describe fixes for bugs found in the new version.
-
- 9.5 _K_n_o_w_n__P_r_o_b_l_e_m_s__a_n_d__W_o_r_k_a_r_o_u_n_d_s
-
- This section lists problems in the new Audio File Library
- software and ways to work around them.
-
- +o AIFF-C comment chunks: the Audio File Library API does
- not yet include routines for reading/writing comment
- chunk data in AIFF-C files. The library routine
- _a_f_O_p_e_n_F_i_l_e(3dm) allows you to open an AIFF-C file that
- contains a comment chunk, but there is no way to access
- the comment chunk fields through library routines.
-
- +o Apple Computer proprietary compression algorithms:
- several compression algorithms described in the AIFF-C
- specification (ACE2, ACE8, MAC3, MAC6) are Apple
- proprietary and cannot be decoded on IRIS workstations.
- To avoid problems with proprietary or unsupported
- compression algorithms, convert AIFF-C files so that
- they contain uncompressed data before attempting to
- transfer them between an IRIS workstation and another
- audio workstation platform.
-
- +o Aware MultiRate codec: An application is only allowed
- to have one AFfilehandle open at a time which uses the
- Aware MultiRate or Lossless compressor or decompressor.
- This applies to the application's entire process share
- group.
-
- Note: This bug does not apply to the Aware MPEG
- implementation.
-
- 9.6 _D_o_c_u_m_e_n_t_a_t_i_o_n__E_r_r_o_r_s
-
- This section lists errors in the Audio File Library
- documentation.
-
- +o The _I_R_I_S _D_i_g_i_t_a_l _M_e_d_i_a _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e has not yet
- been updated to reflect the changes and additions made
- in the new version of the AF. However, the manual
- pages are completely up-to-date and cover every routine
- available in the new AF.
-
-
-
-
-
-
-
-
-
-
-
- - 6 -
-
-
-
-
-
- 9.7 _A_u_d_i_o__F_i_l_e__L_i_b_r_a_r_y_:__D_e_v_e_l_o_p_e_r__N_o_t_e_s
-
- An important warning to developers about using the new Audio
- File Library. Read this in order to assure that your
- applications continue to work well with the new expanded
- version of the library.
-
- 9.7.1 _I_n_t_r_o_d_u_c_t_i_o_n It has come to our attention that some
- applications which use the Audio File Library are making
- untrue assumptions about the library. These assumptions,
- though in conflict with the practice laid out by our
- documentation, happened coincidentally not to cause any
- errors with versions of the library shipped with IRIX 5.3
- and earlier. But it is entirely possible that some of these
- mis-coded applications will fail when run with this new
- release of the library.
-
- As the Audio File Library is a Dynamic Shared Object (DSO),
- this is a matter of some concern to developers, as end-users
- may upgrade OS releases and get a new DSO which breaks their
- applications without the applications' developer having had
- a chance to re-release.
-
- The library released with IRIX 6.2 is a superset of the
- previously-released library. New functionality has been
- added, but all previously-existing functions are still
- present.
-
- This section is provided to warn developers of problems
- which may be experienced with the new release due to
- incorrect assumptions made in application code.
-
- The first problem involves assumptions about the kinds of
- files the library will be able to open. The second and
- third problems have to do with using the Audio Library in
- multi-threaded applications. The fourth problem involves
- the use of an AFfilehandle's file descriptor while the
- filehandle is alive. Then we follow with a warning about
- the correlation between AF calls and actual UNIX operations
- on files (such as read(), write(), and lseek() system
- calls).
-
- 9.7.2 _U_s_e__o_f__t_h_e__O_l_d__V_e_r_s_i_o_n__o_f__t_h_e__A_u_d_i_o__F_i_l_e__L_i_b_r_a_r_y
- Because the confusion described above seems to be fairly
- widespread, and because some of this confusion may have
- stemmed from insufficient emphasis of these points in SGI's
- documentation, the new library has been modified via the SGI
- DSO versioning system so that programs built prior to the
- 6.2 release will still bind to a copy of the old library.
-
-
-
-
-
-
-
-
-
-
-
- - 7 -
-
-
-
- This means that old AF programs will not be able to get the
- feature and performance improvements of the new Audio File
- Library without relinking. But this will also decrease the
- chances that bugs in developers' application code such as
- the ones described below will be exposed, until the
- developer has a chance to repair their application, relink
- to bind to the new DSO, and ship a new version to their
- customers.
-
- We did this version change as a one-time aid for developers.
- We do not intend to create new DSO versions for each new
- audio file library upgrade.
-
- We give no guarantees that the old version of libaudiofile
- will be maintained in the same manner as the new version, or
- in any way whatsoever. But we can guarantee that we will
- not intentionally make changes to it which would cause
- applications with the below pitfalls to fail. Please note
- however that some of the problems below could happen at any
- time, whether libaudiofile changes or not. Some could
- surface to users as a result of a change of any software on
- the system or even a change of system hardware. So just
- because we are keeping this older libaudiofile.so does not
- mean developers have any guarantee whatsoever that their
- programs will continue to execute fine. All developers
- should check their software for these problems and remove
- them as soon as possible.
-
- 9.7.3 _H_o_w__D_o_e_s__T_h_i_s__D_S_O__M_a_g_i_c__W_o_r_k_? The version of the
- Audio File library which has been shipping since IRIX 5.1 is
- stamped with the interface version "sgi1.0". This can be
- seen by looking at the MIPS_IVERSION entry of the DSO using
- "elfdump -L <library>". Similarly, the new version of the
- Audio File Library is stamped "sgi2.0". When a program
- (specifically, a NON-ABI executable) is linked under IRIX
- 5.x and the DSO is specified on the command line using an
- argument such as -laudiofile, ld looks at the currently
- available DSOs, picks the one called libaudiofile.so, and
- places that DSO's name and its interface version stamp in
- the executable's liblist, which can be seen with "elfdump
- -Dl <executable>".
-
- Then at some later time, the executable is run. This could
- be before or after a new version of the Audio File Library
- is installed. The system (specifically, rld) must then find
- a libaudiofile DSO to use which has a major version of 1
- (the major version is the number before the decimal point in
- the interface version stamp "sgi1.0"). If no new version of
- the Audio File Library has been installed, the system will
- immediately find libaudiofile.so, discover that it has an
- interface version of "sgi1.0", and use it. If a new version
-
-
-
-
-
-
-
-
-
-
-
- - 8 -
-
-
-
- of the Audio File Library has been installed as
- libaudiofile.so, the system will find this DSO first, it
- will look and see that the version number is "sgi2.0", and
- it will reject this file. It will then look for another DSO
- called libaudiofile.so.1, since it wants to find major
- version 1 of this DSO. Since we have now installed the old
- DSO libaudiofile.so.1, the system will find this DSO and use
- it.
-
- In order for an old program to bind to the new DSO, the
- programmer must relink it on a system which has version 2 of
- the DSO. This way, "sgi2.0" will be stored in the entry of
- program's liblist for libaudiofile.so. An alternate way to
- make an AF program bind to the new DSO instead of the old
- (1.0) DSO is to set the environment variable _RLD_ARGS to
- '-ignore_version libaudiofile.so', but this will affect all
- AF programs executed with this environment variable set.
-
- DSOs and DSO versioning are covered in dso(5), ld(1),
- rld(1), and other man pages. dso(5) contains references for
- more information.
-
- 9.7.4 _P_R_O_B_L_E_M__1 Assumption that AF only reads and writes a
- certain set of file and data formats.
-
- The AF is a library which now supports several file formats
- (besides AIFF and AIFF-C) and new data formats (besides 2's
- complement integer and compressed data formats).
-
- This is why we always stated in the old man pages that
- certain formats were the "currently supported" formats (man
- afIntro(3dm)), or that certain data formats or chunk
- configurations were possible "from AIFF files" rather than
- "from files".
-
- The Audio File Library has the following behaviors which may
- break applications making the above assumption when a new
- library is released:
-
- int filefmt = afIdentifyFD(fd);
- int filefmt = afGetFileFormat(handle, &version);
-
- Applications use afIdentifyFD for two reasons: seeing
- whether the AF recognizes an audio file, and seeing
- specifically which format the audio file has. The user uses
- afGetFileFormat for the latter purpose (as well as getting
- the format version, if applicable). This section covers the
- pitfalls of each use separately:
-
- CHECK FOR RECOGNITION:
-
-
-
-
-
-
-
-
-
-
-
-
- - 9 -
-
-
-
- The filefmt returned could very well be something other than
- AF_FILE_AIFF or AF_FILE_AIFFC. This version of the library,
- for example, supports six formats. The application should
- make no assumptions about what tokens could be returned.
-
- Therefore code such as:
-
- if (filefmt != AF_FILE_AIFF &&
- filefmt != AF_FILE_AIFFC)
- {
- printf("this file is not supported by the AF library!\n");
- exit(0);
- }
-
- is incorrect because it is quite reasonable for the AF to
- return a value other than one found in the include file.
- Instead, the code should test whether the AF did not
- recognize the file format rather than testing for what it
- thinks is all possible recognized formats. Here's one
- example:
-
- if (filefmt == AF_FILE_UNSUPPORTED ||
- filefmt == AF_FILE_UNKNOWN)
- {
- printf("this file is not supported by the AF library!\n");
- exit(0);
- }
-
- The difference here is that the code acknowledges that there
- are file formats which could be returned which the coder did
- not know about at the time of coding.
-
- GETTING THE FORMAT ITSELF
-
- What about programs which actually care what the file format
- of a file they are opening is? Note that this should be
- rare, because in the AF, the data format of a file's track
- is queried and manipulated totally independently of the
- file's file format. So only programs which actually have to
- make distinctions like "AIFF vs. AIFF-C" need use
- afGetFileFormat or afIdentifyFD for this purpose.
-
- The caveat here, just as stated above, is that the library
- could easily return something other than one of the
- AF_FILE_... tokens found in /usr/include/audiofile.h at the
- time of compilation. This is because the Audio File Library
- is a dynamic shared object (DSO), and thus when newer
- versions of the audio file library supporting new file
- formats are released with new OS's, programs linked at an
- earlier time will automatically pick up the new library, and
- thus could see the tokens corresponding to the newly
-
-
-
-
-
-
-
-
-
-
-
- - 10 -
-
-
-
- supported formats.
-
- Before you start worrying about how to handle this
- situation, make sure you have to worry about the file format
- at all:
-
- Many applications which check the filefmt should really be
- checking the sampfmt and other track parameters of the
- file's track. For example, there is no reason why a program
- which simply reads the audio data out of a 16-bit
- AF_SAMPFMT_TWOSCOMP AIFF file would not be able to function
- just as well reading that kind of data out of a NeXT/Sun
- file, as long as it did not intend to read AIFF-specific
- chunks out of it as well (certain miscs and insts, for
- example). Such a program has no need to call afIdentifyFD
- or afGetFileFormat to get the file format.
-
- But for those applications that do care about the file
- format, code such as this:
-
- switch (afIdentifyFD(fd))
- {
- case AF_FILE_AIFF:
- printf("This is an AIFF file.\n");
- do_aiff_thing();
- break;
- case AF_FILE_AIFFC:
- printf("This is an AIFF-C file.\n");
- do_aiffc_thing();
- break;
- case AF_FILE_UNKNOWN:
- case AF_FILE_UNSUPPORTED:
- printf("this file is not supported by AF library!!\n");
- exit(0);
- default:
- /* assume there has been some programmatic error */
- printf("bad return value from AF library!!\n");
- assert(0);
- }
-
- should be changed to code that at least does this:
-
- switch (afIdentifyFD(fd))
- {
- case AF_FILE_AIFF:
- printf("This is an AIFF file.\n");
- do_aiff_thing();
- break;
- case AF_FILE_AIFFC:
- printf("This is an AIFF-C file.\n");
- do_aiffc_thing();
-
-
-
-
-
-
-
-
-
-
-
- - 11 -
-
-
-
- break;
- case AF_FILE_UNKNOWN:
- case AF_FILE_UNSUPPORTED:
- printf("this file is not supported by AF library!!\n");
- exit(0);
- default:
- printf("this program cannot handle this file format!\n");
- exit(0);
- }
-
- Applications can now query the AF at runtime in order to
- determine which file formats are supported by the version of
- the AF to which the running program has bound. In addition
- to querying the token that is used to refer to each file
- format, users can query for the textual name and other
- characteristics of each format. See the afQuery(3dm) man
- page for a complete description of this technique.
-
- For many applications, getting the textual name of the file
- format is the entire reason they wanted to call afIdentifyFD
- or afGetFileFormat in the first place, and so such
- applications can be now written so that they instantly and
- automatically understand every file format which the
- currently-bound Audio File Library understands, as in:
-
- int filefmt = afIdentifyFD(fd);
- char *name = (char *) afQueryPointer(AF_QUERYTYPE_FILEFMT,
- AF_QUERY_NAME, filefmt, 0, 0);
- printf("This is file has the %s file format.\n", name);
-
- So what guarantees does the developer have?
-
- The library will never support fewer formats. If a user
- uses an AF_FILE_ token that we ship with a sofware release,
- that token and its associated file format will never become
- unsupported, although it may become obsolete (and perhaps
- new features will not be available for it). So for example,
- the library will never stop supporting AF_FILE_AIFF or
- AF_FILE_AIFFC.
-
- The library will always provide the query mechanism. We
- have made every effort to make the query mechanism versatile
- enough that user programs can make intelligent decisions
- about how to deal with and report info on audio files of
- different file formats.
-
- afGetSampleFormat(AFfilehandle, int track, int *sampfmt, int
- *sampwidth);
-
- Here is the single most likely offender that could be found
- in developer's programs. Because the new AF supports
-
-
-
-
-
-
-
-
-
-
-
- - 12 -
-
-
-
- transparent data format conversion, you may not have to use
- this routine at all, and many of the below-mentioned issues
- will go away.
-
- As the man page says, AIFF and AIFF-C files only support
- AF_SAMPFMT_TWOSCOMP files. However, as the library now
- support other file formats, it is quite likely the library
- may return some other sample format. So code that says:
-
- AFfilehandle h = afOpenFile(....);
- if (!h) return;
-
- afGetSampleFormat(h, AF_DEFAULT_TRACK, &sampfmt, &sampwidth);
-
- /* sampfmt could only be AF_SAMPFMT_TWOSCOMP--no need to check! */
-
- switch (sampwidth)
- {
- ... /* see below for note on this part */
- }
-
- is incorrect. It is by no means guaranteed that sampfmt is
- two's complement. Nor is it guaranteed that sampwidth has
- any meaning--the meaning of sampwidth depends on sampfmt.
-
- For example, this version of the Audio File Library can
- transfer IEEE floats and IEEE doubles. For these types,
- sampwidth has no meaning, and it is ignored and unset. The
- code above would behave very unpredictably.
-
- Code should say:
-
- AFfilehandle h = afOpenFile(....);
- if (!h) return;
-
- afGetSampleFormat(h, AF_DEFAULT_TRACK, &sampfmt, &sampwidth);
-
- if (sampfmt != AF_SAMPFMT_TWOSCOMP)
- {
- printf("This program can't read audio files of this "
- sample format\n");
- exit(0);
- }
-
- switch (sampwidth) /* now we know sampwidth is meaningful */
- {
- ... /* see below for note on this part */
- }
-
- Another note on sampwidth: even with AIFF files, the sample
- width is not necessarily a multiple of 8. Generally, this
-
-
-
-
-
-
-
-
-
-
-
- - 13 -
-
-
-
- can be ignored, because audio samples which do not take up
- an integral number of bytes are left-justified inside the
- next larger integral number of bytes (with the remaining
- bits set to 0). But this may be an issue in code such as
- the switch statement above, if it were written:
-
- switch (sampwidth) /* now sampwidth is meaningful */
- {
- case 8: do_8_thing(); break;
- case 16: do_16_thing(); break;
- case 24: do_24_thing(); break;
- case 32: do_32_thing(); break;
- default:
- /* false assumption */
- printf("bad return value from AF library!\n");
- assert(0);
- }
-
- There are two problems here. One is that the code assumes
- the maximum size of samples in files opened by the library
- is 32 bits, which is definitely not true. The second false
- assumption is that the number of bits will be a multiple of
- 8. Better code would be (after checking that sampfmt is an
- integer format):
-
- /* round sampwidth up to nearest number of bytes */
- int nbytes = ( (sampwidth-1) / 8 ) + 1;
- switch (nbytes)
- {
- case 1: do_8_thing(); break;
- case 2: do_16_thing(); break;
- case 3: do_24_thing(); break;
- case 4: do_32_thing(); break;
- default:
- printf("This program can't read audio files of this "
- sample width %d\n", sampwidth);
- exit(0);
- }
-
- One final thing about sample widths--don't forget that there
- is a special case for reading 24-bit integer data. Because
- 3-byte data is so difficult to deal with, the library
- automatically stretches the data out into convenient 4-byte
- quantities in a way compatible with the SGI Audio Library
- (AL). The tricky thing here is that the data is sign-
- extended on the left for this special case of 17-24-bit
- data, whereas the padding that goes on to handle non-
- multiple-of-8 sample widths is done by zero-bit-padding on
- the right. Here are some examples which should make this
- clear, for the case of AF_SAMPFMT_TWOSCOMP:
-
-
-
-
-
-
-
-
-
-
-
-
- - 14 -
-
-
-
- MSB LSB
- byte 3 byte 2 byte 1 byte 0
- aaaaaaaabbbbbbbbccccccccdddddddd | Sample Format
- ---------------------------------+--------------------------------
- dddddd00 | 6-bit data takes 8 bits
- RR | (right pad)
- |
- dddddddd | 8-bit data takes 8 bits
- | (no padding)
- |
- ccccccccdddd0000 | 12-bit data takes 16 bits
- RRRR | (right pad)
- |
- ccccccccdddddddd | 16-bit data takes 16 bits
- | (no padding)
- |
- s<------bbbbbbbbccccccccdddd0000 | 20-bit data takes 32 bits
- SSSSSSSS RRRR | (left sign-extend AND right pad !)
- |
- s<------bbbbbbbbccccccccdddddddd | 24-bit data takes 32 bits
- SSSSSSSS | (left sign-extend)
- |
- aaaaaaaabbbbbbbbccccccccdddddd00 | 30-bit data takes 32 bits
- RR | (right pad)
- |
- aaaaaaaabbbbbbbbccccccccdddddddd | 32-bit data takes 32 bits
- | (no padding)
- |
-
- Unlike file formats, it doesn't make sense to have a runtime
- query of all possible sample formats. The existence of the
- new virtual audio format system described above allows
- applications to specify in which format they wish to receive
- data.
-
- int afGetCompression(AFfilehandle, int);
- void afGetCompressionParams(AFfilehandle, int, int *, AUpvlist);
-
- Obviously, new compression methods will be supported, so the
- same warnings that apply to afGetSampleFormat apply here.
-
- As the library transparently decompresses input data and
- compresses output data, this is not such a big issue. The
- application simply must specify the virtual format desired.
-
- A given already-released codec in our library will not
- change its native sample format, if we have specified what
- that codec's native sample format is. So, for example, if
- you know the file is AF_COMPRESSION_G711_ULAW, then you can
- assume the native format for that codec is
- AF_SAMPFMT_TWOSCOMP, 16 because we said this in a man page.
-
-
-
-
-
-
-
-
-
-
-
- - 15 -
-
-
-
- This is for backwards compatibility. But we do not guarantee
- that all future codecs will generate 16-bit signed integer
- data natively, nor do we guarantee that all future codecs
- will have a single native sample format (some may be
- configurable or may vary depending on what kind of data was
- originally compressed). In every case though, the
- programmer can call afGetSampleFormat to retrieve the sample
- format for the current codec on the file corresponding to
- the given AFfilehandle.
-
- This new version of the library has a runtime query of all
- of the supported compression methods, their textual names,
- their compression ratios (if available), and any other info
- that may be useful in making a runtime choice of codec.
- This is done via afQuery(3dm) in a similar manner to the
- file format name string query above.
-
- int afGetChannels(AFfilehandle, int/*track*/);
-
- May return any number of channels, just like it could even
- with AIFF and AIFF-C. The virtual number of channels may be
- set to any nonzero value.
-
- double afGetRate(AFfilehandle, int/*track*/);
-
- May return any sampling rate, just like it could even with
- AIFF and AIFF-C. Future versions of the library may provide
- transparent conversion mechanisms which allow the programmer
- to deal with files with unexpected sampling rates in a
- simple and clean way.
-
- For now the program should either check that the sampling
- rate of a file opened for reading is one the program can
- deal with, or use the newly- available rate conversion
- routines available in the libdmedia library. See the
- _d_m_A_u_d_i_o_R_a_t_e_C_o_n_v_e_r_t_e_r_C_r_e_a_t_e(3dm) man page and its associated
- man pages for detailed information.
-
- int afGetTrackIDs(AFfilehandle, int */*track ids*/);
-
- May return more than one track. We give no guarantees that
- only one-track formats are supported. An application might
- want to reject such a file, or perhaps use the first track.
- We do not define the correct behavior.
-
- int afGetMarkIDs(AFfilehandle, int/*track*/, int */*ids*/);
- int afGetMarkPosition(AFfilehandle, int/*track*/, int/*markid*/);
- char *afGetMarkName(AFfilehandle, int, int);
-
- May return any configuration or number of marks just as it
- may with an AIFF/AIFF-C file.
-
-
-
-
-
-
-
-
-
-
-
- - 16 -
-
-
-
- Apps should be written to expect and ignore marks they do
- not understand.
-
- int afGetMiscIDs(AFfilehandle, int * /*idlist*/);
- int afGetMiscType(AFfilehandle, int /*miscid*/);
- int afGetMiscSize(AFfilehandle, int /*miscid*/);
-
- May return ANY type of misc chunk.
-
- Apps should be written to expect and ignore miscellaneous
- chunks they do not understand.
-
- Apps should be especially careful not to copy misc chunks
- from one file to another unless they understand the content
- of those misc chunks; the chunk may contain references to
- other parts of the file which the application has modified.
- In this case the chunk in the new file becomes corrupt.
- This is to be avoided. The chunk should not be copied and
- the user should probably be warned.
-
- int afGetAESChannelData(AFfilehandle,int,unsigned char buf[24]);
-
- Will always return data that is to be interpreted as AES
- channel data, but there is never any guarantee that such
- data will be present or not in a given file format (unless
- that file format itself defines AES data as always being
- present).
-
- int afGetInstIDs(AFfilehandle, int */*inst ids*/);
- long afGetInstParamLong(AFfilehandle, int/*inst*/, int /*param*/);
-
- May return more than one inst, unlike the one fixed INST in
- AIFF/AIFF-C files. There are file formats which will likely
- be supported in the future which have different inst
- configurations than AIFF/AIFF-C.
-
- In the case of INST parameters it makes sense to be able to
- query at runtime the parameters of a given file format. In
- a future release, this will be supported with afQuery()
- mechanism.
-
- Apps should, at the very least, be written to expect and
- ignore instrument configurations or instrument parameters
- they do not understand.
-
- int afGetLoopIDs(AFfilehandle, int /*inst*/, int*);
- int afGetLoopMode(AFfilehandle, int/*inst*/, int /*loopid */);
- int afGetLoopStart(AFfilehandle, int/*inst*/, int/*loopid */);
- int afGetLoopEnd(AFfilehandle, int/*inst*/, int/*loopid*/);
- int afGetLoopTrack(AFfilehandle, int/*inst*/, int/*loopid*/);
-
-
-
-
-
-
-
-
-
-
-
-
- - 17 -
-
-
-
- May return any configurations of loops within an inst, not
- just the fixed value of 2 we have in AIFF/AIFF-C files.
- There are file formats which will likely be supported in the
- future which have different loop configurations than
- AIFF/AIFF-C.
-
- Some form of runtime query as to the possible loop
- configurations for a given file format may be made available
- via the afQuery() mechanism.
-
- At any rate, a program should be written to expect and
- ignore loop configurations they do not understand.
-
- 9.7.5 _P_R_O_B_L_E_M__2 Some developers are writing bad multi-
- threaded code that just happens to work with the AF library.
-
- We have caught several different apps trying to do the
- following from multi-threaded code and we imagine that there
- are other apps that do this as well:
-
- ======== Thread 1 ==============|=========== Thread 2 ===========
- | | |
- | some amount of time | | some amount of time
- | no semaphore locking | | no semaphore locking
- | | |
- |
- afSeekFrame(h,track,place1); | afSeekFrame(h,track,place2);
- afReadFrames(h,track,...); | afReadFrames(h,track,...);
- |
- | | |
- | some amount of time | | some amount of time
- | no semaphore locking | | no semaphore locking
- | | |
-
- Both threads are making calls on the same AFfilehandle at
- the same time, and no concern is put into the order in which
- these calls end up being made on the filehandle.
-
- This code is just plain old flat out wrong, it doesn't
- matter if you're using the AF or any other library.
-
- It is entirely possible that the order the calls get
- executed in by the UNIX scheduler is:
-
- afSeekFrame(h,track,place1); |
- | afSeekFrame(h,track,place2);
- afReadFrames(h,track,...); |
- | afReadFrames(h,track,...);
-
- in which case both threads would read the wrong data. UNIX
- offers absolutely, positively no guarantee that this will
-
-
-
-
-
-
-
-
-
-
-
- - 18 -
-
-
-
- not happen unless the programmer uses one of the many
- available process coordination facilities such as
- semaphores.
-
- Furthermore, no amount of work on the part of the AF library
- writers can prevent this situation, because the library has
- no knowledge of what order the programmer really wants the
- operations to come in. We cannot use the pid as a hint
- because some correctly coded programs may want to seek in
- one thread and read in another (correctly coded programs use
- locking to make sure that these operations happen in the
- right order).
-
- Note that although our example only uses the functions
- afSeekFrame() and afReadFrames(), this problem can exist
- with calls to ANY Audio File Library functions, even ones
- which do not directly manipulate audio files (such as
- operations on an AFfilesetup or operations on an
- AFfilehandle that query or set parameters stored in memory).
- In particular, watch out for uncoordinated calls of
- afSyncFile() and afCloseFile(). Making these calls from
- more than one thread simultaneously in an uncoordinated
- fashion seems to be a common error.
-
- Correct code should look like this:
-
- ======== Thread 1 ==============|=========== Thread 2 ===========
- |
- | | |
- | some amount of time | | some amount of time
- | | |
- | | |
- |
- Lock Sempaphore that guards h | Lock Semaphore that guards h
- afSeekFrame(h,track,place1); | afSeekFrame(h,track,place2);
- afReadFrames(h,track,...); | afReadFrames(h,track,...);
- Unlock Semaphore that guards h | Unlock semaphore that guards h
- |
- | | |
- | some amount of time | | some amount of time
- | | |
- | | |
- |
-
- The key thing to realize here is: UNIX guarantees that only
- one of the Lock Semaphore calls will succeed immediately.
- The thread whose lock does not succeed will sit and wait in
- the Lock Semaphore call (and thus not proceed to the
- afSeekFrame) until the other thread has unlocked the
- semaphore (after doing its seek AND its read). Then when
- the other thread unlocks the semaphore, the thread who was
-
-
-
-
-
-
-
-
-
-
-
- - 19 -
-
-
-
- forced to wait will now proceed.
-
- How does one use these semaphores? One uses code such as
- the following to create a semaphore FileOpSema with value 1:
-
- #include <ulocks.h>
-
- AFfilehandle h; /* global file handle */
- usema_t *FileOpSema; /* global semaphore to protect h */
-
- /* initialize semaphore support -- do this once */
- {
- usptr_t *usptr;
- char *arenafile;
-
- /* use fastest (no debugging) form of semaphores -- usconfig (3P) */
- usconfig(CONF_LOCKTYPE, US_NODEBUG);
-
- /* create shared arena to hold the semaphore -- usinit(3P) */
- arenafile = tmpnam(NULL);
- usptr = usinit(arenafile);
-
- /*
- create the semaphore in that arena -- usnewsema(3P)
- create with count 1--there is 1 resource (h), which is
- initially available.
- */
- FileOpSema = usnewsema(usptr,1);
-
- /* we won't need to refer to arena again so we can unlink file */
- unlink(arenafile);
- }
-
- Then one uses uspsema(3P) to lock the semaphore, and
- usvsema(3P) to unlock the semaphore. Pretty simple:
-
- ======== Thread 1 ==============|=========== Thread 2 ===========
- |
- | | |
- | some amount of time | | some amount of time
- | | |
- | | |
- |
- uspsema(FileOpSema); /*lock*/ | uspsema(FileOpSema); /*lock*/
- afSeekFrame(h,track,place1); | afSeekFrame(h,track,place2);
- afReadFrames(h,track,...); | afReadFrames(h,track,...);
- usvsema(FileOpSema); /*unlock*/ | usvsema(FileOpSema); /*unlock*/
- |
- | | |
- | some amount of time | | some amount of time
- | | |
-
-
-
-
-
-
-
-
-
-
-
- - 20 -
-
-
-
- | | |
- |
-
- Now, why are we pointing this out for AF users?
-
- We have found several applications which were actually
- committing this error and were GETTING AWAY WITH IT! By the
- most arcane of coincidences of CPU scheduler timings, these
- apps just happened to never fall into the short window of
- time in which the above worst-case scenario could occur.
-
- When these programs were recompiled with an alpha version of
- a newer Audio File Library, which contains different code
- and thus has slightly different timing characteristics and
- gets scheduled slightly differently, the bug in these
- applications was instantly revealed.
-
- This is why we want to alert developers to check their code
- for this-- there could be a bug of this type in their code
- which had previously not come out, just because the old AF
- happens to have had this property (by no design of our own).
- We want to give developers plenty of advanced warning about
- this situation.
-
- 9.7.6 _P_R_O_B_L_E_M__3 Some developers are writing multi-threaded
- code that would be good under their assumption that the AF
- is MT/MP-safe. But this assumption is false.
-
- The AF is NOT a multi-thread and/or multi-processor safe
- library, in the following sense:
-
- Users can make multiple, simultaneous, uncoordinated AF
- calls on different AFfilehandles from different threads and
- the library will operate fine. Each AFfilehandle completely
- encapsulates the state needed to do operations on that
- AFfilehandle (except for error handling, which is explained
- next).
-
- Users cannot make multiple, simultaneous, uncoordinated AF
- calls from different threads to set or access the library's
- global state--namely, the error handler function. If two
- threads simultaneously try and set the error handler (even
- the same error handler), the behavior is undefined. We will
- likely add a form of MT-safe error handling mechanism in a
- future release of the AF. For example, we may add the
- concept of an error handler that is thread-specific. But we
- cannot offer guarantees that we will provide such a
- mechanism.
-
- Furthermore, if the user writes an error handler, then makes
- multiple, simultaneous, uncoordinated AF calls on different
-
-
-
-
-
-
-
-
-
-
-
- - 21 -
-
-
-
- filehandles from different threads, and both AF calls issue
- an error simultaneously, then two instances of the user's
- error handler will be called in a simultaneous,
- uncoordinated manner in two threads. If this situation is
- possible in a user's program, the user should use semaphores
- in their error handler in order to make sure their handler
- doesn't try and report or deal with two errors at the same
- time. Note that any AF function can cause an AF error to
- occur. Do not assume a function will not err just because
- it is simple.
-
- Now the most important caveat: users cannot make multiple,
- simultaneous, uncoordinated AF calls on the SAME
- AFfilehandle from different threads, even if the order of
- execution of those calls does not matter to the user. Doing
- so will very likely cause a core dump, or at least
- corruption of the AFfilehandle. This behavior will never be
- changed, as we refuse to make our developers pay the price
- of semaphore locking code at the beginning and end of every
- afReadFrames and afWriteFrames call. Most users do not
- need, and in fact really do not want, semaphore protection
- that is built-in to the AF calls themselves.
-
- Like problem 2, problem 3 can occur with any AF call, not
- just afSeekFrame() and afReadFrames(). In particular, watch
- out for simultaneous calls of afSyncFile() and
- afCloseFile().
-
- Note that at no point did we offer any guarantee that the
- library was MT/MP-safe, and in fact only libraries specified
- as such in man pages (such as the C library minus errno, as
- explained in intro(3)) are guaranteed to be so.
-
- 9.7.7 _P_R_O_B_L_E_M__4 Some developers are using afOpenFD() or
- afGetFD() to get the file descriptor which an AFfilehandle
- is using, and then changing the file pointer on that file
- descriptor. The AF does not allow this.
-
- The file descriptor returned by afGetFD() was intended to be
- used as part of a select() loop and was not intended to
- allow users to read, write, and seek in the file without the
- knowledge of the Audio File Library. Doing so will cause
- the library to give unpredictable results unless the user
- saves and restores the file position whenever they modify
- it.
-
- The reason for this is that we are not willing to force upon
- users expecting high performance the often very large
- overhead of an lseek(2) system call on every afReadFrames()
- or afWriteFrames(). Therefore we cannot restore the file
- position to a known place on every AF call which relies on
-
-
-
-
-
-
-
-
-
-
-
- - 22 -
-
-
-
- the file being in that place.
-
- Users who wish to modify the file position of the file
- descriptor given by afGetFD() should save the file position
- and restore it after they are finished and before they make
- the next AF call to read or write data to the file. The new
- routines _a_f_S_a_v_e_F_i_l_e_P_o_s_i_t_i_o_n(3dm) and
- _a_f_R_e_s_t_o_r_e_F_i_l_e_P_o_s_i_t_i_o_n(3dm) have been created expressly for
- this purpose. Alternately they can use one of two different
- file descriptors opened to the same file (dup(2) will not
- work; it gives you two file descriptors which share a file
- offset. The file must be re-opened in order to get a
- separate file descriptor). Also, if users attempt to write
- to the file, no matter how the AFfilehandle was opened, the
- results are undefined.
-
- Developers had asked for a call to get the offset of the
- audio data in an audio file. Two new functions have been
- made available. afGetDataOffset(3dm) returns the offset in
- bytes from the beginning of an open audio file to the first
- audio sample. afGetTrackBytes(3dm) returns the size in
- bytes of the audio data portion of a given track in an audio
- file.
-
- 9.7.8 _W_A_R_N_I_N_G We give no guarantees about the number or
- nature of UNIX system calls that will result from a given AF
- call. In particular, afReadFrames() and afWriteFrames()
- could actually read() or write() any amount of data in the
- file, or could read() or write() more than once in varying
- chunk sizes. Also, afOpenFile(), afSeekFrame(),
- afSyncFile(), afCloseFile(), and other AF functions could
- result in any amount of data being read from or written to
- the file. (but of course the AF will not write to a file
- opened for read access or read from a file opened for write
- access).
-
- Users who are attempting to optimize the I/O in their
- program by managing I/O system call behavior should be aware
- that at this time we offer no guarantees about when the AF
- will perform system calls.
-
- This has not been a problem to our knowledge, but we thought
- it would be a good idea just to make this explicit. At this
- time we offer no guarantees that a read of, say, 50,000
- frames will result in a single UNIX read() system call for
- exactly 50,000 frames worth of data. Although we will
- strive to make this the case whenever it seems beneficial,
- there are certain cases where this is simply impossible.
- For example, reads from or writes to a compressed file will
- generally be split up into a chunksize which is natural for
- the codec being used, and thus possibly result in several
-
-
-
-
-
-
-
-
-
-
-
- - 23 -
-
-
-
- reads or writes to the file. Also, we give no guarantee
- that afSeekFrame() will seek the file at all in the UNIX
- lseek() sense, although it happens that this is generally
- the case for the current Audio File Library. And even if
- afSeekFrame() does lseek() the file, we do not guarantee
- that it will be to the exact frame the user specified
- (though this is generally the case in the old AF library).
- This is due to necessary compensation for filter delays
- inherent in certain forms of compression and decompression.
-
- In a future release, features will be introduced which may
- make this more of an issue. At that time we will come up
- with much more specific information about what guarantees we
- can offer to the developer. We want to find out if these
- kinds of guarantees are something that developers need at
- all. Perhaps it is simply not an issue for developers. But
- if it is, we would be most interested in hearing your
- feedback. You can contact your local SGI representative and
- ask them to send a message to the developers of the Audio
- File Library, or you can also use the newsgroup
- comp.sys.sgi.audio for faster and much more direct contact.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-